package org.yaac.server.egql.evaluator.function;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.yaac.server.egql.evaluator.EvaluationResult;
import org.yaac.server.egql.evaluator.Evaluator;
import org.yaac.server.egql.exception.EGQLE003Exception;
import org.yaac.server.egql.exception.EGQLException;
import org.yaac.server.egql.processor.ProcessData.ProcessDataRecord;
import org.yaac.shared.ErrorCode;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public class KeyFunction extends Function {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public void validate() throws EGQLException {
		int paramSize = this.ops.size();
		if (paramSize == 0 || (paramSize != 1 && paramSize % 2 != 0)) {
			throw new EGQLE003Exception();
		}
	}

	@Override
	public EvaluationResult evaluate(ProcessDataRecord record) {
		if (this.ops.isEmpty()) {
			return new EvaluationResult(null).withWarning(ErrorCode.W124);
		} else if (this.ops.size() == 1) {	// KEY('encoded key')
			EvaluationResult r = this.ops.get(0).evaluate(record);
			if (r.getPayload() instanceof String) {
				try {
					return new EvaluationResult(
							KeyFactory.stringToKey((String) r.getPayload()), r);	
				} catch (IllegalArgumentException e) {	// invalid input key string
					return r.withWarning(ErrorCode.W129);
				}
			} else {
				return r.withWarning(ErrorCode.W125);
			}
		} else if (this.ops.size() % 2 == 0) { //TODO KEY('kind', 'name'/ID [, 'kind', 'name'/ID...]) 
			Key key = null;
			
			// evaluation results, used at last to construct all previous results
			List<EvaluationResult> rs = new ArrayList<EvaluationResult>(this.ops.size());
			
			Iterator<Evaluator> i = this.ops.iterator();
			while (i.hasNext()) {
				EvaluationResult kindR = i.next().evaluate(record);
				EvaluationResult nameIdR = i.next().evaluate(record);
				
				if (!(kindR.getPayload() instanceof String)) {
					return kindR.withWarning(ErrorCode.W126);
				}
				
				if (nameIdR.getPayload() instanceof String) {
					key = KeyFactory.createKey(key, 
							(String)kindR.getPayload(), 
							(String)nameIdR.getPayload());
				} else if (nameIdR.getPayload() instanceof Number) {
					key = KeyFactory.createKey(key, 
							(String)kindR.getPayload(), 
							((Number)nameIdR.getPayload()).longValue());
				} else {
					return kindR.withWarning(ErrorCode.W127);	
				}
				
				rs.add(kindR);
				rs.add(nameIdR);
			}
			
			EvaluationResult [] rsArray = new EvaluationResult[rs.size()];
			rs.toArray(rsArray);
			return new EvaluationResult(key, rsArray);
		} else {
			return this.ops.get(0).evaluate(record).withWarning(ErrorCode.W128);
		}
	}
}
